#include <windows.h>
#include <dbghelp.h>
#include <process.h>
#include <ctime>
#include <iostream>
#include <fstream>


#include "Game.h"
#include "CInputManager.h"


const char* sz_Window_Class_Name = "TSSEngine";
const char* sz_Window_Title = "TSS Engine";
int nWindow_Width = 1024;
int nWindow_Height = 768;

HWND hWnd;


Game* game = NULL;


#ifndef _DEBUG
const bool bIs_Windowed = TRUE;
#else
const bool bIs_Windowed = TRUE;//FALSE = windowless
#endif


void SaveOptions()
{
	std::ofstream fout("Resource/Bin/Options.ini");
	if(fout.is_open())
	{
		RECT rc;
		GetClientRect( hWnd, &rc );
		int width = rc.right - rc.left;
		int height = rc.bottom - rc.top;
		fout << width << std::endl;
		fout << height << std::endl;

		fout.close();
	}
}


LONG WINAPI Handler(_EXCEPTION_POINTERS * exPointers)
{
	HANDLE theFile = CreateFileA("TSSMiniDump.mdmp", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

	if (theFile != INVALID_HANDLE_VALUE)
	{
		_MINIDUMP_EXCEPTION_INFORMATION exInfo;
		exInfo.ThreadId = GetCurrentThreadId();
		exInfo.ClientPointers = NULL;
		exInfo.ExceptionPointers = exPointers;

		MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), theFile, MiniDumpNormal, &exInfo, NULL, NULL);
		MessageBoxA(0, "The game saved a miniDump file (TSSMiniDump.mdmp) please try the game again. And send us the miniDump so we can fix the crash.", "Game Crash", MB_OK);
	}
	return EXCEPTION_EXECUTE_HANDLER;
}




int main(int argc, char*argv[])
{
	WinMain(GetModuleHandle(NULL), NULL, GetCommandLine(), SW_SHOWDEFAULT);


	std::cout << std::endl << std::endl;

	system("pause");

	return 0;
}


LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hdc;

	switch(msg)
	{
	case WM_SYSKEYUP:
	case WM_SYSCHAR:
		return 0;
		break;
	case WM_CLOSE:
		{
			SaveOptions();
			DestroyWindow(hWnd);
		}
		break;
	case WM_ACTIVATE:
		{
			if(LOWORD(wParam) != WA_INACTIVE)
			{
				//unpause the game

				//When in windowed mode and you click outside of the game it was losing all input from then on
				//Nate added this to fix that problem as this message means that we are regaining focus and should grab all the devices
				//again. However if there are crashes or problems this can be removed we will then need to come up with another method
				//of handling this problem.
				if(game->GetGameLoaded())
				{
					CInputManager::GetInstance()->AquireAll();
				}
			}
			else
			{
				//pause the game
				/*if(CGame::GetInstance()->GetGameLoaded())
				{
					if(CGame::GetInstance()->GetCurrentState() == CGameState::GetInstance())
					{
						CGame::GetInstance()->PushState(CPauseState::GetInstance());
					}
				}*/
			}
		}
		break;
	case WM_CREATE:
		{
			return 0;
		}
		break;
	case WM_PAINT:
		{
			hdc = BeginPaint(hWnd, &ps);

			EndPaint(hWnd, &ps);
			return 0;
		}
		break;
	case WM_DESTROY:
		{
			SaveOptions();
			PostQuitMessage(0);
			return 0;
		}
		break;
	default:
		break;
	}

	return DefWindowProc(hWnd, msg, wParam, lParam);
}


bool CheckIfAlreadyRunning()
{
	hWnd = FindWindow(sz_Window_Class_Name, sz_Window_Title);

	if(hWnd)
	{
		//If its minimized restore it
		if(IsIconic(hWnd))
		{
			ShowWindow(hWnd, SW_RESTORE);
		}

		SetForegroundWindow(hWnd);

		return true;
	}

	return false;
}


bool RegisterWindowClass(HINSTANCE hInstance)
{
	WNDCLASSEX winClass;

	winClass.cbSize = sizeof(winClass);
	winClass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
	winClass.lpfnWndProc = WindowProc;
	winClass.cbClsExtra = 0;
	winClass.cbWndExtra = 0;
	winClass.hInstance = hInstance;
	winClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	winClass.hIconSm = NULL;
	winClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	winClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	winClass.lpszMenuName = NULL;
	winClass.lpszClassName = sz_Window_Class_Name;

	return RegisterClassEx(&winClass);
}


HWND MakeWindow(HINSTANCE hInstance)
{
	DWORD dwWindowStyleFlags = WS_VISIBLE;

	if(bIs_Windowed)
	{
		dwWindowStyleFlags |= WS_OVERLAPPEDWINDOW;
		ShowCursor(FALSE); //CHANGE TO TRUE TO SEE WINDOWS CURSOR
	}
	else
	{
		dwWindowStyleFlags |= WS_POPUP;
		ShowCursor(FALSE); //CHANGE TO TRUE TO SEE WINDOWS CURSOR
	}

	RECT rWindow;
	rWindow.left = 0;
	rWindow.top = 0;
	rWindow.right = nWindow_Width;
	rWindow.bottom = nWindow_Height;

	AdjustWindowRectEx(&rWindow, dwWindowStyleFlags, FALSE, WS_EX_APPWINDOW);

	int nWindowWidth = rWindow.right - rWindow.left;
	int nWindowHeight = rWindow.bottom - rWindow.top;

	return CreateWindowEx(WS_EX_APPWINDOW, sz_Window_Class_Name, sz_Window_Title, dwWindowStyleFlags, (GetSystemMetrics(SM_CXSCREEN) / 2) - (nWindowWidth / 2), (GetSystemMetrics(SM_CYSCREEN) / 2) - (nWindowHeight / 2), nWindowWidth, nWindowHeight, NULL, NULL, hInstance, NULL);
}


void LoadOptions()
{
	//read in options here and set screen width / height
	
	std::ifstream fin("Resource/Bin/Options.ini");
	if(fin.is_open())
	{
		fin >> nWindow_Width;
		fin >> nWindow_Height;

		fin.close();
	}

	if(nWindow_Width <= 0)
	{
		nWindow_Width = 1024;
	}
	if(nWindow_Height <= 0)
	{
		nWindow_Height = 768;
	}
}


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
	MSG msg;

	srand((unsigned int)time(0));

	LoadOptions();


	//make sure only one instance is running
	if(!hPrevInstance)
	{
		if(CheckIfAlreadyRunning())
			return FALSE;
	}

	if(!RegisterWindowClass(hInstance))
		return 0;

	hWnd = MakeWindow(hInstance);

	if(!hWnd)
		return 0;



	ShowWindow(hWnd, nShowCmd);
	UpdateWindow(hWnd);


	//setup the mini dump
	//SetUnhandledExceptionFilter(Handler);



	//initialize game
	game = Game::GetInstance();
	game->StartUp(hWnd, hInstance, nWindow_Width, nWindow_Height, bIs_Windowed);


	while(TRUE)
	{
		if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			if(msg.message == WM_QUIT)
				break;

			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		//game logic here
		if(!game->Main())
			PostQuitMessage(0);


	}


	//shutdown game here
	game->ShutDown();

	UnregisterClass(sz_Window_Class_Name, hInstance);



	return (int)msg.wParam;
}